/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package com.icee.myth.manager;

import com.icee.myth.common.AbstractServer;
import com.icee.myth.common.channelContext.HeartbeatChannelContext;
import com.icee.myth.common.message.serverMessage.InternalChannelMessage;
import com.icee.myth.common.message.serverMessage.Message;
import com.icee.myth.common.messageQueue.ServerMessageQueue;
import com.icee.myth.manager.message.serverMessage.DeamonHeartbeatManagerMessage;
import com.icee.myth.manager.message.serverMessage.builder.ManagerMessageBuilder;
import com.icee.myth.protobuf.builder.DeamonToManagerBuilder;
import com.icee.myth.utils.LogConsts;
import com.icee.myth.utils.MLogger;
import org.jboss.netty.bootstrap.ServerBootstrap;
import org.jboss.netty.channel.Channel;

import java.util.HashMap;
import java.util.Map.Entry;

/**
 *
 * @author liuxianke
 */
public class DeamonToManagerServer extends AbstractServer {

    private int port; // Server的端口地址
    private ServerBootstrap bootstrap;
    private Channel bindChannel = null;

    private HashMap<String, HeartbeatChannelContext> channelContexts = new HashMap<String, HeartbeatChannelContext>();  // 记录与Client(服务器端相互间的client)连接的channel context，key是client的ip

    //Singleton
    private static DeamonToManagerServer INSTANCE = new DeamonToManagerServer();

    //getInstance操作的第一次调用在Main的start中，因此无需同步
    public static DeamonToManagerServer getInstance() {
        return INSTANCE;
    }

    private DeamonToManagerServer() {
    }

    @Override
    public void closeServer() {
        super.closeServer();

        for (HeartbeatChannelContext channelContext : channelContexts.values()) {
            channelContext.close();
        }
    }

    private void addClient(String channelIP, Channel channel) {
        HeartbeatChannelContext newChannelContext = new HeartbeatChannelContext();
        newChannelContext.setChannel(channel);
        newChannelContext.restoreHeartbeat();
        
        channelContexts.put(channelIP, newChannelContext);
    }

    protected void heartbeat() {
        for (Entry<String, HeartbeatChannelContext> entry : channelContexts.entrySet()) {
            HeartbeatChannelContext channelContext = entry.getValue();
            
            // 向对方发送心跳消息
            channelContext.write(buildServerHeartBeat());

            // 心跳计数减一
            int beatNum = channelContext.heartbeat();
            // 心跳计数小于等于0表示发生故障
            if (beatNum <= 0) {
                // 当心跳计数为0时，向消息队列中产生一服务故障消息。
                String deamonIP = entry.getKey();
                if (beatNum == 0) {
                    ServerMessageQueue.queue().offer(buildDeamonDownMessage(deamonIP));

                    channelContext.close();
                }
                // TODO: 报警，当心跳计数小于等于0后产生连续的报警
                MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Deamon["+deamonIP+"] channel is down!");
            }
        }
    }

    private void clientClose(Channel channel) {
        String deamonIP = channel.getRemoteAddress().toString();
        deamonIP = deamonIP.substring(1, deamonIP.indexOf(":"));

        MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Deamon["+deamonIP+"] channel is close!");

        HeartbeatChannelContext channelContext = channelContexts.get(deamonIP);
//        assert (channelContext != null);

        if (channelContext != null) {
            if (channelContext.isChannel(channel)) {
                channelContext.close();
                channelContexts.remove(deamonIP);
            }
        } else {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Deamon["+deamonIP+"] channel not exist!");
        }
    }

    public HeartbeatChannelContext getChannelContext(String deamonIP) {
        return channelContexts.get(deamonIP);
    }

    public void broadcast(Object msg) {
        for (HeartbeatChannelContext channelContext : channelContexts.values()) {
            channelContext.write(msg);
        }
    }

    public void flush() {
        for (HeartbeatChannelContext channelContext : channelContexts.values()) {
            channelContext.flush();
        }
    }

    private Object buildServerHeartBeat() {
        return DeamonToManagerBuilder.buildManagerHeartbeat();  // TODO: 用静态对象
    }

    private Message buildDeamonDownMessage(String deamonIP) {
        return ManagerMessageBuilder.buildDeamonDownMessage(deamonIP);
    }

    public void handleMessage(Message message) {
        switch (message.getType()) {
            case ALL_HEARTBEAT: {
                heartbeat();
                break;
            }
            case MANAGER_DEAMON_HEARTBEAT: {
                // 对方有心跳，重置心跳计数
                DeamonHeartbeatManagerMessage deamonHeartbeatManagerMessage = (DeamonHeartbeatManagerMessage)message;
                HeartbeatChannelContext channelContext = channelContexts.get(deamonHeartbeatManagerMessage.deamonIP);
                channelContext.restoreHeartbeat();
                break;
            }
            case MANAGER_DEAMON_CONNECT: {
                // 处理Deamon Connect消息
                InternalChannelMessage channelMessage = (InternalChannelMessage)message;

                Channel channel = channelMessage.channel;
                String channelIP = channel.getRemoteAddress().toString();
                channelIP = channelIP.substring(1, channelIP.indexOf(":"));
                // TODO: 判断连接的ip是否合法
                if (channelContexts.containsKey(channelIP)) {
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Multi add Deamon : "+channelIP);
                } else {
                    MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_DEBUG, "Add Deamon : "+channelIP);
                    
                    addClient(channelIP, channel);
                }
                break;
            }
            case MANAGER_DEAMON_CLOSE: {
                // 处理Deamon Close消息
                clientClose(((InternalChannelMessage)message).channel);
                break;
            }
            default:
                System.out.println("unknow case");
                break;
        }
    }
}
